package zhuzx.scheduler;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 运行器，初始化各个任务并提供静态的启动停止方法给使用者调用。
 * @author	zhuzx	2016年1月5日
 */
public class Launcher {
	public static final DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
	public static final DateFormat dateFo = new SimpleDateFormat("yyyy-MM-dd");
	public static final DateFormat timeFo = new SimpleDateFormat("HH:mm:ss");
	public static final long MS_OF_DAY = 1000 * 60 * 60 * 24;
	public static final long MS_OF_WEEK = MS_OF_DAY * 7;
	/**
	 * 任务调用器容器，以任务名为键
	 */
	private static final Map<String,MultitaskInvoker> tasks = new HashMap<String,MultitaskInvoker>();
	/**
	 * 调度器容器，以任务名为键
	 */
	private static final Map<String,ScheduledExecutorService> schedulers =
			new HashMap<String,ScheduledExecutorService>();
	/**
	 * 参数表，以任务名为键
	 */
	private static final Map<String, Map<String,String>> params = new HashMap<String, Map<String,String>>();
	
	static {
		Properties prop = new Properties();
		String packagePath = Launcher.class.getPackage().getName();
		String fileName = packagePath.replaceAll("\\.", "/") + "/" + "TaskConfig.properties";
		InputStream is = Launcher.class.getClassLoader().getResourceAsStream(fileName);
		try {
			prop.load(new InputStreamReader(is, "UTF-8"));
		} catch (IOException e) {
			e.printStackTrace();
		}
		for (Object task : prop.keySet()) {
			String taskName = task.toString();
			String[] paramStr = prop.getProperty(taskName).split(",");
			for (int i=0; i<paramStr.length; i++) {
				if (paramStr[i].trim().length() == 0) {
					paramStr[i] = null;
				}
			}
			int taskSerial = Integer.parseInt(paramStr[0]) - 1;
			MultitaskInvoker mi = new MultitaskInvoker(new DemoJob(taskSerial));
			tasks.put(taskName, mi);
			Map<String,String> taskParam = new HashMap<String,String>();
			taskParam.put("flag", paramStr[1]);
			taskParam.put("period", paramStr[2]);
			taskParam.put("startTime", paramStr[3]);
			taskParam.put("endTime", paramStr[4]);
			params.put(taskName, taskParam);
			//启动配置参数中指定的自启动任务
			if ("1".equals(paramStr[5])) {
				schedulers.put(taskName, Executors.newSingleThreadScheduledExecutor());
				startWeeklyScheduler(taskName, Integer.parseInt(paramStr[6]), paramStr[3], paramStr[4]);
			}
		}
	}
	
	/**
	 * 手动启动任务，按已经设定好的配置运行
	 * @param taskName 预配置的任务名
	 */
	public static void startTask(String taskName) {
		System.out.println(taskName + "开始");
		MultitaskInvoker mi = tasks.get(taskName);
		Map<String,String> taskParam = params.get(taskName);
		byte flag = Byte.parseByte(taskParam.get("flag"));
		long period = Long.parseLong(taskParam.get("period"));
		if (flag == 1) {
			mi.startTaskSerial(period, parse(taskParam.get("startTime")), parse(taskParam.get("endTime")));
		} else if (flag == 2) {
			mi.startTaskSerial(period);
		} else if (flag == 3) {
			mi.startTaskParallelBlocking(period);
		} else if (flag == 4) {
			mi.startTaskParallelRechecking(4);
		} else {
			System.out.println("\n\n" + ">>>>不合法的标志位！" + "\n\n");
		}
	}
	
	/**
	 * 手动停止任务
	 * @param taskName 预配置的任务名
	 */
	public static void stopTask(String taskName) {
		tasks.get(taskName).shutdown();
		System.out.println(taskName + "结束");
	}
	
	/**
	 * 启动调度器
	 * @param taskName 预配置的任务名
	 * @param duration 持续时间
	 * @param initialDelay 初始延迟
	 * @param period 调度周期
	 */
	public static void startScheduler(final String taskName, final long duration, long initialDelay, long period) {
		ScheduledExecutorService ses = schedulers.get(taskName);
		if (ses == null) {
			ses = Executors.newSingleThreadScheduledExecutor();
			schedulers.put(taskName, ses);
		}
		ses.scheduleAtFixedRate(
				new Runnable() {
					public void run() {
						startTask(taskName);
						try {
							Thread.sleep(duration);
						} catch (InterruptedException e) {
							Thread.currentThread().interrupt();
						}
						stopTask(taskName);
					}
				},
				initialDelay, period, TimeUnit.MILLISECONDS);
	}
	
	/**
	 * 停止调度器
	 * @param taskName 预配置的任务名
	 */
	public static void stopScheduler(String taskName) {
		schedulers.get(taskName).shutdown();
		schedulers.put(taskName, null);
		tasks.get(taskName).shutdown();
		System.out.println(">>>>" + taskName + "的调度程序终止");
	}
	
	/**
	 * 启动调度器，以周为时间单位
	 * @param taskName 预配置的任务名
	 * @param dayOfWeek 星期几
	 * @param startTime 每天开始的时间，格式为HH:mm:ss
	 * @param endTime 结束时间
	 */
	public static void startWeeklyScheduler(String taskName, int dayOfWeek, String startTime, String endTime) {
		//Calendar类的星期是从星期天开始排的，星期天、星期一、星期二……
		dayOfWeek++;
		dayOfWeek = (dayOfWeek == 8) ? 1 : dayOfWeek;
		Calendar targetTime = Calendar.getInstance();
		targetTime.set(Calendar.DAY_OF_WEEK, dayOfWeek);
		setTime(targetTime, startTime);
		long initialDelay = targetTime.getTimeInMillis() - System.currentTimeMillis();
		if (initialDelay < 0) {
			initialDelay += MS_OF_WEEK;
		}
		long duration = subtractTime(startTime, endTime);
		startScheduler(taskName, duration, initialDelay, MS_OF_WEEK);
	}
	
	/**
	 * 启动调度器，以天为时间单位
	 * @param taskName 预配置的任务名
	 * @param startTime 每天开始的时间，格式为HH:mm:ss
	 * @param endTime 结束时间
	 */
	public static void startDailyScheduler(String taskName, String startTime, String endTime) {
		Calendar targetTime = Calendar.getInstance();
		setTime(targetTime, startTime);
		long initialDelay = targetTime.getTimeInMillis() - System.currentTimeMillis();
		if (initialDelay < 0) {
			initialDelay += MS_OF_DAY;
		}
		long duration = subtractTime(startTime, endTime);
		startScheduler(taskName, duration, initialDelay, MS_OF_DAY);
	}
	
	/**
	 * 日期时间字符串转Date
	 */
	public static Date parse(String time) {
		Date date = null;
		try {
			if (time.length() == 8) {
				date = timeFo.parse(time);
			} else if (time.length() == 10) {
				date = dateFo.parse(time);
			} else {
				date = df.parse(time);
			}
		} catch (ParseException e) {
			e.printStackTrace();
		}
		return date;
	}
	
	/**
	 * 计算两个时间之间的毫秒差
	 */
	public static long subtractTime(String startTime, String endTime) {
		return parse(endTime).getTime() - parse(startTime).getTime();
	}
	
	/**
	 * 设置时分秒
	 * @param cal 要修改的日历类对象
	 * @param time 格式为HH:mm:ss
	 */
	public static void setTime(Calendar cal, String time) {
		String[] hms = time.split(":");
		cal.set(Calendar.HOUR_OF_DAY, Integer.parseInt(hms[0]));
		cal.set(Calendar.MINUTE, Integer.parseInt(hms[1]));
		cal.set(Calendar.SECOND, Integer.parseInt(hms[2]));
	}
	
}
